/*
 * Copyright 2009, eGDP LTD, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.egdp.jclpvm.core.ioc;

import static com.nativelibs4java.opencl.OpenCL4Java.createContext;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.nativelibs4java.opencl.CLContext;
import com.nativelibs4java.opencl.CLDevice;

/**
 * Implements abstract of proxied "CLContext component".
 * Final bean will be dynamically injected into application context.
 *
 * @author Alex Sorkin alexander.sorkin@gmail.com
 * @since 0.0.1
 * @see ProxyCLPlatform
 * @see ProxyCLDevice
 */
public abstract class ProxyCLContext implements ICLContainerContext {

	protected static Log logger = LogFactory.getLog(ProxyCLContext.class);
			
	@Autowired
	protected ICLContainerDevice[] jclpvm_devices;
	private final PropertyChangeSupport changes  = new PropertyChangeSupport( this );
	
	private CLContext legal_clcontext = null; //wrapped legal cl_context
	
	protected static CLDevice[] jclDevs2LegDev(ICLContainerDevice[] jclpvmDevices){
		CLDevice[] retLegDevs = new CLDevice[jclpvmDevices.length];
		if (jclpvmDevices.length > 0){
			int i = 0;
			for (ICLContainerDevice jclpvmDevice: jclpvmDevices){
				retLegDevs[i] = jclpvmDevice.getLegal_cldevice();
				i++;
			}
		}else{
			logger.debug("No supported devices found at this point");
			logger.debug("Possibly due to wrong context injection order/dependencies");
		}
		return retLegDevs;
	}
	
	/**
	 * @param jclpvmDevices
	 */
	public ProxyCLContext(ICLContainerDevice[] jclpvmDevices) {
		super();
		this.jclpvm_devices = jclpvmDevices;
		this.legal_clcontext = createContext(jclDevs2LegDev(jclpvmDevices));
		logger.debug("Legal context has created: \n\tValue: " + legal_clcontext);
	}

	/**
	 * @param jclpvmDevices the jclpvm_devices to set
	 */
	protected void setJclpvm_devices(ICLContainerDevice[] jclpvmDevices) {
		if (jclpvmDevices != null){
			ICLContainerDevice[] old_jclpvm_devices = this.jclpvm_devices;
			this.jclpvm_devices = jclpvmDevices;
	
			CLContext old_legal_clcontext = legal_clcontext;
			CLContext new_legal_clcontext = createContext(jclDevs2LegDev(jclpvmDevices));
			logger.debug("Informing PropertyChange listeners about legal context being changed");
			this.changes.firePropertyChange("legal_clcontext", old_legal_clcontext, new_legal_clcontext);
			
			// releasing associated devices and legal context
			logger.info("Releasing associated devices");
			if (old_jclpvm_devices.length > 0){
				for (ICLContainerDevice old_jclpvm_device: old_jclpvm_devices)
					old_jclpvm_device.releaseLegal_cldevice();
			}else{
				logger.debug("No associated devices has found");
			}
			logger.info("Releasing current legal context");
			releaseLegal_clcontext();
			legal_clcontext = new_legal_clcontext;
			logger.info("Legal context has changed: \n\tOld_value: " + old_legal_clcontext + "\n\tNew_value: " + new_legal_clcontext);
		}else{
			logger.error("Device list must not be null, can't change context");
		}
	}

	/**
	 * @return the jclpvm_devices
	 */
	protected ICLContainerDevice[] getJclpvm_devices() {
		return jclpvm_devices;
	}

	/**
	 * @return the legal_clcontext
	 */
	public CLContext getLegal_clcontext() {
		return legal_clcontext;
	}	
	
	public void releaseLegal_clcontext(){
		if (legal_clcontext != null)
			legal_clcontext.release();
		logger.debug("Current legal context has released");
	}
	
	/**
	 * To get callback support with depended beans
	 */
	public void addPropertyChangeListener(final PropertyChangeListener l) {  
        this.changes.addPropertyChangeListener( l );  
    }   
	public void removePropertyChangeListener(final PropertyChangeListener l) {  
        this.changes.removePropertyChangeListener( l );  
    }
}
